#ifndef __BE_DATABASE__
#define __BE_DATABASE__

#include <iostream>

#define SQL_RESULT std::vector< std::vector<String> >

template<int _t>
struct SqlValue;

template<>
struct SqlValue<SQLITE_INTEGER>
{
  enum {
    type = SQLITE_INTEGER,
  };
  
  long long value;
  inline void load(sqlite3_stmt *_stmt, int _col)
  {
    value = sqlite3_column_int64(_stmt, _col);
  }
};


template<>
struct SqlValue<SQLITE_FLOAT>
{
  enum {
    type = SQLITE_FLOAT,
  };

  double value;
  inline void load(sqlite3_stmt *_stmt, int _col)
  {
    value = sqlite3_column_double(_stmt, _col);
  }
};

template<>
struct SqlValue<SQLITE_TEXT>
{
  enum {
    type = SQLITE_TEXT,
  };

  String value;
  inline void load(sqlite3_stmt *_stmt, int _col)
  {
    value = (const char*)sqlite3_column_text(_stmt, _col);
  }
};

template<>
struct SqlValue<SQLITE_NULL>
{
  enum {
    type = SQLITE_NULL,
  };

  inline void load(sqlite3_stmt *_stmt, int _col)
  {
    std::cout << "null" << std::endl;
  }
};

template<int _t, int _i>
struct SqlResult2
{
  enum
    {
      size = _i,
    };

  typedef std::vector< SqlResult2<_t, _i> > Array;
  SqlValue<_t> value[_i];
};


template<int _t1, int _t2, int _t3, int _t4, int _t5, int _t6, int _t7>
struct SqlResult
{
  typedef std::vector< SqlResult<_t1, _t2, _t3, _t4, _t5, _t6, _t7> > Array;
  SqlValue<_t1> first;
  SqlValue<_t2> second;
  SqlValue<_t3> third;
  SqlValue<_t4> fourth;
  SqlValue<_t5> fifth;
  SqlValue<_t6> sixth;
  SqlValue<_t7> seventh;
};

class Database
{
 private:
  sqlite3 *database;

						
  template<class _C, int _i>
    struct Loader
    {
      static void getValue( _C &_result, sqlite3_stmt *_statement )
      {
	Loader<_C, _i - 1>::getValue( _result, _statement );
	_result.value[_i - 1].load(_statement, _i - 1);
      }
    };

  template<class _C>
    struct Loader<_C, 1>
    {
      static void getValue( _C &_result, sqlite3_stmt *_statement )
      {
	_result.value[0].load(_statement, 0);
      }
    };

 public:
  Database();
  ~Database();

  bool open(const char* _filename);

  template<class _C>
    bool query2( const String &_sql, _C &_result )
    {
      sqlite3_stmt *statement = NULL;
      int r, cols;
      
      typename _C::value_type tmp_result;
      
      if ( sqlite3_prepare_v2( database, _sql.c_str(), -1, &statement, 0 ) != SQLITE_OK )
	{
	  return false;
	}

      cols = sqlite3_column_count(statement);
      if ( cols > _C::value_type::size )
	cols = _C::value_type::size;

      while(true)
	{
	  r = sqlite3_step(statement);

	  if ( r == SQLITE_ROW )
	    {
	      Loader<typename _C::value_type, _C::value_type::size>::getValue(tmp_result, statement);
	      _result.push_back(tmp_result);
	    }
	  else
	    {
	      break;
	    }
	}
      
      sqlite3_finalize(statement);
      return true;
    }
  
  template<int _t1, int _t2, int _t3, int _t4, int _t5, int _t6, int _t7>
    bool query( const String &_sql, typename SqlResult<_t1, _t2, _t3, _t4, _t5, _t6, _t7>::Array &_result )
  {
    sqlite3_stmt *statement = NULL;
    int r, cols;

    SqlResult<_t1, _t2, _t3, _t4, _t5, _t6, _t7> tmp_result;
    
    if ( sqlite3_prepare_v2( database, _sql.c_str(), -1, &statement, 0 ) != SQLITE_OK )
      {
	return false;
      }

    cols = sqlite3_column_count(statement);

    while(true)
      {
	r = sqlite3_step(statement);

	if ( r == SQLITE_ROW )
	  {
	    if ( cols > 7 ) cols = 7;

	    switch(cols)
	      {
	      case 7:
		tmp_result.seventh.load(statement, 6);

	      case 6:
		tmp_result.sixth.load(statement, 5);
		
	      case 5:
		tmp_result.fifth.load(statement, 4);
		
	      case 4:
		tmp_result.fourth.load(statement, 3);
		
	      case 3:
		tmp_result.third.load(statement, 2);
		
	      case 2:
		tmp_result.second.load(statement, 1);
		
	      case 1:
		tmp_result.first.load(statement, 0);
	      }
	    
	    _result.push_back(tmp_result);
	  }
	else
	  {
	    break;
	  }
      }

    sqlite3_finalize(statement);

    return true;
  }
  
};

#endif

